package com.mszlu.blog.common.aop;

import com.alibaba.fastjson.JSON;
import com.mszlu.blog.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

@Component
@Aspect //切面  定义了通知和切点的关系
@Slf4j
public class CacheAspect {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Pointcut("@annotation(com.mszlu.blog.common.aop.Cache)")
    public void pt(){}

    //环绕通知
    @Around("pt()")
    public Object dos(ProceedingJoinPoint joinPoint) throws Throwable {

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        //类名,方法名
        String className = joinPoint.getTarget().getClass().getName();  //类名
        String methodName = signature.getName();    //方法名

        //请求参数
        Class[] parameterTypes = new Class[joinPoint.getArgs().length];
        String params = "";
        Object[] args = joinPoint.getArgs();
        for (int i=0 ; i<args.length ;i++) {
            if (args[i] != null) {
                params += JSON.toJSONString(args[i]);
                parameterTypes[i] = args[i].getClass();
            }else {
                parameterTypes[i] = null;
            }
        }

        if (StringUtils.isNotEmpty(params)){
            //加密  以防出现key过长以及字符转义获取不到的情况
            params = DigestUtils.md5DigestAsHex(params.getBytes());
        }
        Method method = joinPoint.getSignature().getDeclaringType().getMethod(methodName,parameterTypes);

        //获取Cache注解
        Cache annotation = method.getAnnotation(Cache.class);
        //缓存过期时间
        long expire = annotation.expire();
        //缓存名称
        String name = annotation.name();

        //先从redis中获取
        String redisKey = name + "::" + className + "::" + methodName + "::" + params;
        String redisValue = redisTemplate.opsForValue().get(redisKey);

        if (StringUtils.isNotEmpty(redisValue)) {
            log.info("走了缓存~~~ ,{},{}", className, methodName);
            return JSON.parseObject(redisValue, Result.class);
        }

        //redisValue为空      走原始方法
        Object result = joinPoint.proceed();
        String value = JSON.toJSONString(result);
        redisTemplate.opsForValue().set(redisKey, value, expire, TimeUnit.SECONDS);
        log.info("存入缓存~~~ {},{}", className, methodName);
        return result;
    }


}
